﻿using jvm.classfile;
using jvm.classfile.attributeinfo;
using jvm.rtda.heap.clazz.cp;
using jvm.rtda.heap.clazz.method;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace jvm.rtda.heap.clazz
{
    class JMethod : ClassMember
    {
        public uint maxStack;
        public uint maxLocals;
        public byte[] code;
        public uint argCount;
        public LineNumberTableAttribute lineNumberTable;
        public ExceptionTable exceptionTable;

        public JMethod(JClass jClass, JMemberInfo memberInfo) : base(jClass, memberInfo)
        {
            MethodDescriptorParser parser = new MethodDescriptorParser(descriptor);
            MethodDescriptor methodDescriptor = parser.Parse();
            CalcArgCount(methodDescriptor);
            if (!Is(AccessFlags.ACC_NATIVE))
            {
                CopyCode(memberInfo);
            }
            else
            {
                InjectCode(methodDescriptor);
            }
        }

        void CalcArgCount(MethodDescriptor methodDescriptor)
        {
            
            foreach (string t in methodDescriptor.paramTypes)
            {
                argCount++;
                if (t.Equals("J") || t.Equals("D"))
                {
                    argCount++;
                }
            }
            if (!Is(AccessFlags.ACC_STATIC))
            {
                argCount++; // 非静态方法的第一个参数是this
            }
        }

        void CopyCode(JMemberInfo memberInfo)
        {
            CodeAttribute codeAttr = memberInfo.CodeAttribute();
            if (codeAttr != null)
            {
                maxStack = codeAttr.maxStack;
                maxLocals = codeAttr.maxLocals;
                code = codeAttr.code;
                lineNumberTable = codeAttr.LineNumberTableAttribute;
                exceptionTable = new ExceptionTable(codeAttr.exceptionTable, jClass.constantPool);
            }
        }

        void InjectCode(MethodDescriptor methodDescriptor)
        {
            maxStack = 40;
            maxLocals = argCount;
            switch (methodDescriptor.retType[0])
            {
                case 'V':
                    code = new byte[] {0xfe, 0xb1};
                    break;
                case 'L':
                case '[':
                    code = new byte[] { 0xfe, 0xb0 };
                    break;
                case 'D':
                    code = new byte[] { 0xfe, 0xaf };
                    break;
                case 'F':
                    code = new byte[] { 0xfe, 0xae };
                    break;
                case 'J':
                    code = new byte[] { 0xfe, 0xad };
                    break;
                default:
                    code = new byte[] { 0xfe, 0xac };
                    break;
            }
        }

        public int FindExceptionHandler(JClass exceptionClass, int pc)
        {
            ExceptionHandler handler = exceptionTable.FindExceptionHandler(exceptionClass, pc);
            if (handler != null)
            {
                return handler.handlerPc;
            }
            return -1;
        }

        public int GetLineNumber(int pc)
        {
            if (Is(AccessFlags.ACC_NATIVE))
            {
                return -2;
            }
            if (lineNumberTable == null)
            {
                return -1;
            }
            return lineNumberTable.GetLineNumber(pc);
        }
    }
}
